package top.codef.serializers;

import java.io.IOException;
import java.math.BigDecimal;
import java.text.DecimalFormat;
import java.text.ParseException;

import javax.money.CurrencyUnit;
import javax.money.Monetary;
import javax.money.MonetaryAmount;
import javax.money.UnknownCurrencyException;

import org.javamoney.moneta.FastMoney;
import org.javamoney.moneta.RoundedMoney;

import com.fasterxml.jackson.core.JsonParser;
import com.fasterxml.jackson.core.JsonProcessingException;
import com.fasterxml.jackson.core.JsonToken;
import com.fasterxml.jackson.databind.BeanProperty;
import com.fasterxml.jackson.databind.DeserializationContext;
import com.fasterxml.jackson.databind.JsonDeserializer;
import com.fasterxml.jackson.databind.JsonMappingException;
import com.fasterxml.jackson.databind.deser.ContextualDeserializer;
import com.fasterxml.jackson.databind.deser.std.StdDeserializer;

import top.codef.MercuryException;
import top.codef.anno.Money;
import top.codef.enums.MoneyFormatStyle;
import top.codef.money.interfaces.MercuryMonetaryAmountFormat;
import top.codef.pojos.MoneyObjectField;
import top.codef.properties.MercuryProperties;

public abstract class AbstractMoneyDeserializer<T extends MonetaryAmount> extends StdDeserializer<T>
		implements ContextualDeserializer {

	private static final long serialVersionUID = 978798645775503284L;

	protected final MercuryProperties mercuryProperties;

	protected MoneyFormatStyle style = MoneyFormatStyle.DEFAULT;

	protected final MercuryMonetaryAmountFormat monetaryAmountFormat;

	public AbstractMoneyDeserializer(Class<?> vc, MercuryProperties mercuryProperties,
			MercuryMonetaryAmountFormat monetaryAmountFormat) {
		super(vc);
		this.mercuryProperties = mercuryProperties;
		this.monetaryAmountFormat = monetaryAmountFormat;
	}

	public AbstractMoneyDeserializer(StdDeserializer<?> src, MercuryProperties mercuryProperties,
			MercuryMonetaryAmountFormat monetaryAmountFormat) {
		super(src);
		this.mercuryProperties = mercuryProperties;
		this.monetaryAmountFormat = monetaryAmountFormat;
	}

	public AbstractMoneyDeserializer(StdDeserializer<?> src, MercuryProperties mercuryProperties,
			MoneyFormatStyle style, MercuryMonetaryAmountFormat monetaryAmountFormat) {
		super(src);
		this.mercuryProperties = mercuryProperties;
		this.style = style;
		this.monetaryAmountFormat = monetaryAmountFormat;
	}

	@Override
	public JsonDeserializer<?> createContextual(DeserializationContext ctxt, BeanProperty property)
			throws JsonMappingException {
		AbstractMoneyDeserializer<?> deserializer = this;
		if (property != null) {
			Money money = property.getAnnotation(Money.class);
			Class<?> clazz = property.getType().getRawClass();
			MoneyFormatStyle style = MoneyStyles.in(money);
			if (MonetaryAmount.class != clazz && money != null && style != MoneyFormatStyle.DEFAULT) {
//				System.out.println("需要创建：" + clazz + "   " + style);
				deserializer = withStyle(style, clazz);
			}
		}
		return deserializer;
	}

	private AbstractMoneyDeserializer<?> withStyle(MoneyFormatStyle value, Class<?> clazz) {
		if (clazz == FastMoney.class || clazz == MonetaryAmount.class)
			return new DefaultMoneyDeserializer(this, mercuryProperties, value, monetaryAmountFormat);
		else if (clazz == org.javamoney.moneta.Money.class)
			return new DefaultMoneyDeserializer(this, mercuryProperties, value, monetaryAmountFormat);
		else if (clazz == RoundedMoney.class)
			return new DefaultMoneyDeserializer(this, mercuryProperties, value, monetaryAmountFormat);
		throw new MercuryException("不支持的类型");
	}

	protected abstract T genericMoney(MonetaryAmount monetaryAmount);

	@Override
	public T deserialize(JsonParser p, DeserializationContext ctxt) throws IOException, JsonProcessingException {
		MonetaryAmount money = null;
		switch (style) {
		case DEFAULT:
			money = monetaryAmountFormat.parse(p.getValueAsString());
			break;
		case NUM_ONLY:
			String moneyNum = p.getValueAsString();
			try {
				money = moneyNum == null ? null
						: FastMoney.of(DecimalFormat.getInstance(mercuryProperties.currentLocale()).parse(moneyNum),
								Monetary.getCurrency(mercuryProperties.currentLocale()));
			} catch (ParseException e1) {
				throw new MercuryException("金额解析错误", e1);
			}
			break;
		case OBJECT:
			String amount = null;
			CurrencyUnit currencyUnit = Monetary.getCurrency(mercuryProperties.currentLocale());
			if (p.currentToken() == JsonToken.START_OBJECT) {
				while (p.nextToken() != JsonToken.END_OBJECT) {
					String currentField = p.currentName();
					if (MoneyObjectField.AMMOUNT.contentEquals(currentField))
						amount = p.nextTextValue();
					else if (MoneyObjectField.CURRENTCY.contentEquals(currentField))
						try {
							currencyUnit = Monetary.getCurrency(p.nextTextValue());
						} catch (UnknownCurrencyException e) {
							currencyUnit = null;
						}
				}
			}
			if (amount != null && currencyUnit != null) {
				money = FastMoney.of(new BigDecimal(amount), currencyUnit);
			}
		}
		T resultMoney = money == null ? null : genericMoney(money);
		return resultMoney;
	}
}
